using MapsterMapper;
using AiQiuQuan.Sport.Model.Models;
using AiQiuQuan.Sport.Core;
using AiQiuQuan.Sport.WebApi.Entities;
using AiQiuQuan.Sport.WebApi.Repositories;
using Microsoft.EntityFrameworkCore;
using AiQiuQuan.Sport.Model.Enums;
using AiQiuQuan.Sport.Core.Tool;
using RedLockNet;
using AiQiuQuan.Sport.WebApi.Core;
using AiQiuQuan.Sport.Model;

namespace AiQiuQuan.Sport.WebApi.Services.Impl
{
    /// <summary>
    /// 活动管理
    /// </summary>
    public class ActivityService : IActivityService
    {
        private readonly IMapper _mapper;
        private readonly ICurrentInfo _currentInfo;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IActivityRepository _activityRepository;
        private readonly IActivityUserRepository _activityUserRepository;
        private readonly IBaseUserRepository _baseUserRepository;
        private readonly IBaseUserExtendInfoRepository _baseUserExtendInfoRepository;
        private readonly IBaseUserTennisLevelConfigRepository _baseUserTennisLevelConfigRepository;
        private readonly IDistributedLockFactory _distributedLockFactory;
        private readonly IGameOrderRepository _gameOrderRepository;

        /// <summary>
        /// ctor
        /// </summary>
        public ActivityService(
            IMapper mapper,
            ICurrentInfo currentInfo,
            IUnitOfWorkManager unitOfWorkManager,
            IActivityRepository activityRepository,
            IActivityUserRepository activityUserRepository,
            IBaseUserRepository baseUserRepository,
            IBaseUserExtendInfoRepository baseUserExtendInfoRepository,
            IBaseUserTennisLevelConfigRepository baseUserTennisLevelConfigRepository,
            IDistributedLockFactory distributedLockFactory,
            IGameOrderRepository gameOrderRepository)
        {
            _mapper = mapper;
            _currentInfo = currentInfo;
            _unitOfWorkManager = unitOfWorkManager;
            _activityRepository = activityRepository;
            _activityUserRepository = activityUserRepository;
            _baseUserRepository = baseUserRepository;
            _baseUserExtendInfoRepository = baseUserExtendInfoRepository;
            _baseUserTennisLevelConfigRepository = baseUserTennisLevelConfigRepository;
            _distributedLockFactory = distributedLockFactory;
            _gameOrderRepository = gameOrderRepository;
        }

        /// <summary>
        /// 查询一条记录
        /// </summary>
        public async Task<ActivityDto> GetAsync(Guid id)
        {
            var activity = await _activityRepository.AsNoTracking().FirstOrDefaultAsync(m => m.ID == id);
            var result = _mapper.Map<Activity, ActivityDto>(activity);

            return result;
        }

        /// <summary>
        /// 查询一条记录
        /// </summary>
        public async Task<ActivityDto> QueryAsync(ActivitDetailRequestDto request)
        {
            var activity = await _activityRepository.AsNoTracking().FirstOrDefaultAsync(m => m.ID == request.ID);
            var joinStateList = new List<GameJoinStateEnum> { GameJoinStateEnum.Joining, GameJoinStateEnum.JoinCompleted };
            var result = _mapper.Map<Activity, ActivityDto>(activity);
            if (result != null)
            {
                result.Distance = DistanceTool.GetDistanceKM(result.Lat, result.Lng, request.Lat, request.Lng);
                var queryable = from activityUser in _activityUserRepository.AsNoTracking()
                                join user in _baseUserRepository.AsNoTracking()
                                on activityUser.UserId equals user.ID
                                where activityUser.ActivityId == activity.ID && joinStateList.Contains(activityUser.JoinState)
                                select new ActivityUserSimpleDto
                                {
                                    ActivityId = activity.ID,
                                    GameOrderId = activityUser.GameOrderId,
                                    JoinState = activityUser.JoinState,
                                    UserId = activityUser.UserId,
                                    UserCode = user.U_Code,
                                    UserName = user.U_NickName,
                                    HeadUrl = user.U_Headportrait,
                                    CreateTime = activityUser.CreateTime
                                };
                var sql = queryable.Take(4).ToQueryString();
                result.ActivityUserList = await queryable.Take(4).ToListAsync();
            }

            return result;
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        public async Task<PageResult<ActivityDto>> PageListAsync(ActivityPageReqestDto request)
        {
            var queryable = _activityRepository.AsNoTracking();
            if (request.IsOnlySelf.GetValueOrDefault())
            {
                var stateList = new List<GameJoinStateEnum> { GameJoinStateEnum.Joining, GameJoinStateEnum.JoinCompleted };
                queryable = queryable.Where(m => _activityUserRepository.Any(j => j.ActivityId == m.ID && j.UserId == _currentInfo.UserId && stateList.Contains(j.JoinState)));
            }

            if (!string.IsNullOrWhiteSpace(request.Name))
            {
                queryable = queryable.Where(m => m.Name.Contains(request.Name));
            }

            if (request.State.HasValue && request.State.Value != 0)
            {
                queryable = queryable.Where(m => m.State == request.State.Value);
            }

            if (request.ProvinceId.HasValue && request.CityId.HasValue)
            {
                queryable = queryable.Where(m => m.ProvinceId == request.ProvinceId && m.CityId == request.CityId);
            }
            else if (request.ProvinceId.HasValue && !request.CityId.HasValue)
            {
                queryable = queryable.Where(m => m.ProvinceId == request.ProvinceId);
            }

            var pagelist = await queryable.OrderByDescending(m => m.CreateTime).PageListAsync<Activity, ActivityDto>(request, _mapper);
            foreach (var item in pagelist.Result)
            {
                item.Distance = DistanceTool.GetDistanceKM(item.Lat, item.Lng, request.Lat, request.Lng);
            }

            return pagelist;
        }

        /// <summary>
        /// 新增
        /// </summary>
        public async Task<UnaryResult> AddAsync(ActivityAddDto model)
        {
            model.IsClub ??= false;
            var activity = _mapper.Map<Activity>(model);
            activity.State = GameStateEnum.Joining;
            activity.IsClub = model.IsClub.Value;
            await _activityRepository.AddAsync(activity);
            return UnaryResult.Succeed();
        }

        /// <summary>
        /// 修改
        /// </summary>
        public async Task<UnaryResult> UpdateAsync(ActivityUpdateDto model)
        {
            var activity = await _activityRepository.FirstOrDefaultAsync(m => m.ID == model.ID);
            if (activity == null)
            {
                return UnaryResult.Faild(10, "未查询到活动");
            }

            if (activity.State != GameStateEnum.Joining)
            {
                return UnaryResult.Faild(10, "非活动报名状态");
            }

            _mapper.Map(model, activity);
            await _activityRepository.UpdateAsync(activity);

            return UnaryResult.Succeed();
        }

        /// <summary>
        /// 修改活动状态
        /// </summary>
        public async Task<UnaryResult> UpdateStateAsync(ActivityStateUpdateDto model)
        {
            var activity = await _activityRepository.FirstOrDefaultAsync(m => m.ID == model.ID);
            if (activity == null)
            {
                return UnaryResult.Faild(10, "未查询到活动");
            }

            if (activity.State == GameStateEnum.Finished || activity.State == GameStateEnum.Cancel)
            {
                return UnaryResult.Faild(10, $"活动{activity.State.ToEnumDescription()}");
            }

            activity.State = model.State;
            await _activityRepository.UpdateAsync(activity);

            return UnaryResult.Succeed();
        }

        /// <summary>
        /// 删除
        /// </summary>
        public async Task<UnaryResult> DeleteAsync(Guid id)
        {
            var list = new List<GameJoinStateEnum> { GameJoinStateEnum.Joining, GameJoinStateEnum.JoinCompleted };
            if (await _activityUserRepository.AnyAsync(m => m.ActivityId == id && list.Contains(m.JoinState)))
            {
                return UnaryResult.Faild(10, "已有用户报名，不能删除");
            }

            await _activityRepository.DeleteAsync(p => p.ID == id);
            return UnaryResult.Succeed();
        }

        /// <summary>
        /// 活动用户列表查询
        /// </summary>
        /// <param name="activityId">活动ID</param>
        public async Task<List<ActivityUserSimpleDto>> QueryActivityUserListAsync(Guid activityId)
        {
            var joinStateList = new List<GameJoinStateEnum> { GameJoinStateEnum.Joining, GameJoinStateEnum.JoinCompleted };
            var queryable = from activityUser in _activityUserRepository.AsNoTracking()
                            join user in _baseUserRepository.AsNoTracking()
                            on activityUser.UserId equals user.ID into userd
                            from user in userd.DefaultIfEmpty()
                            join tennis1 in _baseUserExtendInfoRepository.AsNoTracking()
                            on user.ID equals tennis1.ID into tennis1d
                            from tennis1 in tennis1d.DefaultIfEmpty()
                            join tennis2 in _baseUserTennisLevelConfigRepository.AsNoTracking()
                            on tennis1.BaseUserTennisLevelConfigID equals tennis2.ID into tennis2d
                            from tennis2 in tennis2d.DefaultIfEmpty()
                            where activityUser.ActivityId == activityId && joinStateList.Contains(activityUser.JoinState)
                            select new ActivityUserSimpleDto
                            {
                                ActivityId = activityUser.ActivityId,
                                GameOrderId = activityUser.GameOrderId,
                                JoinState = activityUser.JoinState,
                                UserId = activityUser.UserId,
                                JoinUserId = activityUser.JoinUserId,
                                UserCode = user.U_Code,
                                UserName = user.U_NickName,
                                HeadUrl = user.U_Headportrait,
                                CreateTime = activityUser.CreateTime,
                                TennisLevel = tennis2.LevelName ?? "1.0",
                                PhoneNO = user.U_PhoneNum
                            };
            var result = await queryable.ToListAsync();
            result.ForEach(item =>
            {
                item.IsOrderCreator = _currentInfo.UserId == item.JoinUserId;
            });
            return result;
        }

        /// <summary>
        /// 活动用户列表查询,通过订单ID
        /// </summary>
        /// <param name="orderId">订单ID</param>
        public async Task<List<ActivityUserSimpleDto>> QueryActivityUserListByOrderIdAsync(Guid orderId)
        {
            var queryable = from activityUser in _activityUserRepository.AsNoTracking()
                            join user in _baseUserRepository.AsNoTracking()
                            on activityUser.UserId equals user.ID into userd
                            from user in userd.DefaultIfEmpty()
                            where activityUser.GameOrderId == orderId
                            select new ActivityUserSimpleDto
                            {
                                ActivityId = activityUser.ActivityId,
                                GameOrderId = activityUser.GameOrderId,
                                JoinState = activityUser.JoinState,
                                UserId = activityUser.UserId,
                                JoinUserId = activityUser.JoinUserId,
                                UserCode = user.U_Code,
                                UserName = user.U_NickName,
                                HeadUrl = user.U_Headportrait,
                                CreateTime = activityUser.CreateTime,
                                PhoneNO = user.U_PhoneNum
                            };
            var result = await queryable.ToListAsync();
            result.ForEach(item =>
            {
                item.IsOrderCreator = _currentInfo.UserId == item.JoinUserId;
            });
            return result;
        }

        /// <summary>
        /// 活动报名
        /// </summary>
        public async Task<UnaryResult<ActivityJoinResultDto>> JoinAsync(ActivityUserAddDto request)
        {
            var result = new ActivityJoinResultDto { ActivityId = request.ActivityId };
            var currentUser = await _baseUserRepository.AsNoTracking().FirstOrDefaultAsync(m => m.ID == _currentInfo.UserId);
            await using (var redlock = await _distributedLockFactory.CreateLockAsync(request.ActivityId.ToString("N"), TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(8), TimeSpan.FromSeconds(1)))
            {
                if (!redlock.IsAcquired)
                {
                    return UnaryResult.From(10, result, "报名通道拥挤，稍后重试");
                }

                var activity = await _activityRepository.FirstOrDefaultAsync(m => m.ID == request.ActivityId);
                //验证
                var unaryResult = await ValidJoinAsync(activity, request, result);
                if (unaryResult.Code != 0)
                {
                    return unaryResult;
                }

                //数据整理
                activity.TotalCount += request.UserList.Count;
                var order = CreateOrder(activity, request, currentUser);
                var activityUserList = new List<ActivityUser>();
                request.UserList.ForEach(m =>
                {
                    activityUserList.Add(new ActivityUser
                    {
                        ActivityId = activity.ID,
                        UserId = m.ID,
                        JoinState = GameJoinStateEnum.Joining,
                        PayState = PayOrderStateEnum.NotPay,
                        JoinUserId = currentUser.ID,
                        GameOrderId = order.ID,
                    });
                });

                //保存
                using (var uow = _unitOfWorkManager.Begin())
                {
                    await _activityRepository.UpdateAsync(activity);
                    await _activityUserRepository.BulkAddAsync(activityUserList);
                    await _gameOrderRepository.AddAsync(order);
                    await uow.CompleteAsync();
                }

                result.ActivityOrderId = order.ID;
                result.ActivityOrderNo = order.OrderNo;
            }

            return UnaryResult.Succeed(result);
        }

        #region private methods
        /// <summary>
        /// 活动业务订单
        /// </summary>
        private GameOrder CreateOrder(Activity activity, ActivityUserAddDto request, BaseUser user)
        {
            return new GameOrder
            {
                ID = Guid.NewGuid(),
                Name = activity.Name,
                BusinessId = activity.ID,
                BusinessType = BusinessTypeEnum.Activity,
                OrderNo = Tool.BuildPayOrderNo(BusinessTypeEnum.Activity),
                GameDate = activity.StartDate,
                StartTime = activity.StartTime,
                EndTime = activity.EndTime,
                Address = activity.Address,
                PayType = PayTypeEnum.None,
                State = GameOrderStateEnum.NotPay,
                JoinCount = request.UserList.Count,
                Price = activity.Amount,
                OrderAmount = request.UserList.Count * activity.Amount,
                PayAmount = request.UserList.Count * activity.Amount,
                Bonus = 0,
                BonusAmount = 0,
                CouponId = null,
                CouponAmount = 0,
                Remark = $"{activity.Name}报名:{user.U_RealName ?? user.U_NickName}",
                RefundAmount = 0,
                RefundRemark = null,
                UserId = user.ID,
                Mobile = user.U_PhoneNum,
                UserName = user.U_RealName ?? user.U_NickName
            };
        }

        /// <summary>
        /// 活动报名验证
        /// </summary>
        private async Task<UnaryResult<ActivityJoinResultDto>> ValidJoinAsync(Activity activity, ActivityUserAddDto request, ActivityJoinResultDto result)
        {
            if (activity == null)
            {
                return UnaryResult.From(10, result, "未查询到活动");
            }

            if (activity.State != GameStateEnum.Joining)
            {
                return UnaryResult.From(10, result, "活动非报名中状态");
            }

            var activityUserList = await _activityUserRepository.Where(m => m.ActivityId == request.ActivityId).ToListAsync();
            if (activityUserList.Any())
            {
                ActivityUser activityUer = null;
                foreach (var user in request.UserList)
                {
                    activityUer = activityUserList.FirstOrDefault(m => m.UserId == user.ID);
                    if (activityUer != null)
                    {
                        switch (activityUer.JoinState)
                        {
                            case GameJoinStateEnum.Joining:
                            case GameJoinStateEnum.JoinCompleted:
                                return UnaryResult.From(10, result, $"重复报名");
                            case GameJoinStateEnum.Refused:
                            case GameJoinStateEnum.Cancel:
                            default:
                                return UnaryResult.From(10, result, $"拒绝取消用户不能再次报名");
                        }
                    }
                }
            }

            if (activity.LimitCount < activity.TotalCount + request.UserList.Count)
            {
                return UnaryResult.From(10, result, $"活动人数超限，已报名:{activity.TotalCount}");
            }

            return UnaryResult.Succeed(result);
        }
        #endregion
    }
}
